package edu.uwlax.cs.oayonlinestore.service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;

import org.apache.commons.collections.map.HashedMap;

import edu.uwlax.cs.oayonlinestore.vo.OrderItemVO;
import edu.uwlax.cs.oayonlinestore.vo.ProductVO;
import edu.uwlax.cs.oayonlinestore.vo.SalesReportDetailsVO;
import edu.uwlax.cs.oayonlinestore.vo.SalesReportProductVO;
import edu.uwlax.cs.oayonlinestore.vo.SalesReportVO;

public class SalesReport {

	private static class ProductHash extends HashedMap {

		private static final long serialVersionUID = 4847030043569790100L;

		private HashMap products = new HashMap();

		public DetailsHash get(ProductVO key) {
			Integer k = new Integer(key.getId());
			DetailsHash r = (DetailsHash) super.get(k);
			if (r == null) {
				put(k, r = new DetailsHash());
				products.put(k, key);
			}
			return r;
		}

		public ProductVO getProduct(Object key) {
			return (ProductVO) products.get(key);
		}
	}

	private static class DetailsHash extends HashedMap {

		private static final long serialVersionUID = 3039326637230613052L;

		private class Key {
			private float discount;

			private float price;

			public Key(float price, float discount) {
				super();
				this.discount = discount;
				this.price = price;
			}

			public int hashCode() {
				return Float.floatToIntBits(discount)
						+ Float.floatToIntBits(price);
			}

			public boolean equals(Object obj) {
				if (this == obj)
					return true;
				if (obj == null)
					return false;
				if (getClass() != obj.getClass())
					return false;
				final Key other = (Key) obj;
				if (Float.floatToIntBits(discount) != Float
						.floatToIntBits(other.discount))
					return false;
				if (Float.floatToIntBits(price) != Float
						.floatToIntBits(other.price))
					return false;
				return true;
			}

		}

		public SalesReportDetailsVO get(float price, float discount) {
			Key k = new Key(price, discount);
			SalesReportDetailsVO r = (SalesReportDetailsVO) super.get(k);
			if (r == null)
				put(k, r = new SalesReportDetailsVO(0, price, discount));
			return r;
		}
	}

	private static float itemPrice(int quantity, float price, float discount) {
		float disc = price * (discount / 100);
		return quantity * (price - disc);
	}

	private static float tax(float price) {
		return price * 0.055f; // TODO introduce central place for tax
	}

	public static SalesReportVO generateSalesReport(Collection orderItems) {
		ProductHash hash = new ProductHash();
		for (Iterator i = orderItems.iterator(); i.hasNext();) {
			OrderItemVO oi = (OrderItemVO) i.next();
			SalesReportDetailsVO d = hash.get(oi.getProduct()).get(
					oi.getPrice(), oi.getDiscount());
			d.setQuantity(d.getQuantity() + oi.getQuantity());
		}
		ArrayList list = new ArrayList();
		float totalPrice = 0;
		float totalTax = 0;
		for (Iterator i = hash.keySet().iterator(); i.hasNext();) {
			SalesReportProductVO rp = new SalesReportProductVO();
			Object id = i.next();
			rp.setProduct(hash.getProduct(id));
			rp.setDetails(new ArrayList(((DetailsHash) hash.get(id)).values()));
			float price = 0, tax;
			for (Iterator j = rp.getDetails().iterator(); j.hasNext();) {
				SalesReportDetailsVO det = (SalesReportDetailsVO) j.next();
				price += itemPrice(det.getQuantity(), det.getPrice(), det
						.getDiscount());
			}
			price += tax = tax(price);
			totalPrice += price;
			totalTax += tax;
			rp.setTotal(price);
			rp.setTax(tax);
			list.add(rp);
		}
		return new SalesReportVO(totalTax, totalPrice, null, null, list);
	}
}
